using Microsoft.Extensions.Logging;
using System.Data;
using System.Data.Common;
using System.Text;

namespace SeataNet.Core.AT.UndoLog
{
    /// <summary>
    /// UndoLog执行器接口
    ///
    /// 定义undo_log回滚执行的标准接口。
    ///
    /// 职责：
    /// - 根据undo_log记录生成反向SQL并执行
    /// - 将数据恢复到事务执行前的状态
    ///
    /// 工作原理：
    /// 1. 从undo_log中读取before/after image
    /// 2. 根据SQL类型生成相应的反向SQL：
    ///    - INSERT -> DELETE (删除新增的数据)
    ///    - UPDATE -> UPDATE (恢复到修改前的值)
    ///    - DELETE -> INSERT (重新插入删除的数据)
    /// 3. 执行反向SQL恢复数据
    ///
    /// 使用场景：
    /// - 第二阶段回滚：TC通知RM回滚时调用
    /// - 数据恢复：根据undo_log恢复数据到事务前状态
    /// </summary>
    public interface IUndoLogExecutor
    {
        /// <summary>
        /// 执行回滚操作
        ///
        /// 根据undo_log记录生成并执行反向SQL，恢复数据到事务前状态。
        ///
        /// 执行流程：
        /// 1. 检查undoLogRecord是否为空
        /// 2. 根据SqlType判断原始操作类型
        /// 3. 生成相应的反向SQL并执行：
        ///    - INSERT -> 执行DELETE删除新增的数据
        ///    - UPDATE -> 执行UPDATE恢复到before image
        ///    - DELETE -> 执行INSERT重新插入删除的数据
        /// 4. 记录回滚结果
        ///
        /// 注意事项：
        /// - 必须在数据库事务中执行
        /// - before/after image必须包含主键值
        /// - 回滚操作应该是幂等的（可以重复执行）
        /// - 如果数据被其他事务修改（脏写），回滚会失败
        /// </summary>
        /// <param name="connection">数据库连接，必须已打开并在事务中</param>
        /// <param name="undoLogRecord">undo_log记录，包含before/after image</param>
        Task ExecuteRollbackAsync(DbConnection connection, UndoLogRecord undoLogRecord);
    }

    /// <summary>
    /// UndoLog执行器默认实现
    ///
    /// 职责：
    /// 1. 实现IUndoLogExecutor接口
    /// 2. 根据undo_log生成反向SQL并执行
    /// 3. 支持INSERT/UPDATE/DELETE三种SQL类型的回滚
    /// 4. 提供SQL构建的辅助方法
    ///
    /// 回滚逻辑详解：
    /// ```
    /// INSERT回滚（ExecuteDeleteForInsertAsync）：
    /// 1. 读取AfterImage（INSERT执行后的数据）
    /// 2. 根据主键构建WHERE条件
    /// 3. 生成DELETE SQL：DELETE FROM table WHERE pk=?
    /// 4. 执行DELETE删除新增的数据
    ///
    /// UPDATE回滚（ExecuteUpdateForUpdateAsync）：
    /// 1. 读取BeforeImage（UPDATE执行前的数据）
    /// 2. 构建SET子句（非主键列）
    /// 3. 构建WHERE子句（主键列）
    /// 4. 生成UPDATE SQL：UPDATE table SET col1=?, col2=? WHERE pk=?
    /// 5. 执行UPDATE恢复到修改前的值
    ///
    /// DELETE回滚（ExecuteInsertForDeleteAsync）：
    /// 1. 读取BeforeImage（DELETE执行前的数据）
    /// 2. 构建列名列表和参数列表
    /// 3. 生成INSERT SQL：INSERT INTO table (col1, col2) VALUES (?, ?)
    /// 4. 执行INSERT重新插入删除的数据
    /// ```
    ///
    /// 线程安全：
    /// - 无状态实现，线程安全
    /// - 每次操作使用传入的connection
    /// - 不维护内部状态或缓存
    ///
    /// 使用示例：
    /// ```csharp
    /// var executor = new DefaultUndoLogExecutor(logger);
    ///
    /// using (var connection = CreateConnection())
    /// using (var transaction = connection.BeginTransaction())
    /// {
    ///     // 查询undo_log记录
    ///     var undoLogRecord = DeserializeUndoLogRecord(undoLog.RollbackInfo);
    ///
    ///     // 执行回滚
    ///     await executor.ExecuteRollbackAsync(connection, undoLogRecord);
    ///
    ///     transaction.Commit();
    /// }
    /// ```
    ///
    /// 注意事项：
    /// - 所有回滚操作必须在数据库事务中执行
    /// - before/after image必须包含完整的主键信息
    /// - 参数化查询防止SQL注入
    /// - 记录详细的执行日志便于排查问题
    /// </summary>
    public class DefaultUndoLogExecutor : IUndoLogExecutor
    {
        private readonly ILogger<DefaultUndoLogExecutor> _logger;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="logger">日志记录器</param>
        public DefaultUndoLogExecutor(ILogger<DefaultUndoLogExecutor> logger)
        {
            _logger = logger;
        }

        /// <summary>
        /// 执行回滚操作（核心方法）
        ///
        /// 根据undo_log记录的SQL类型，调用相应的回滚方法恢复数据。
        ///
        /// 执行流程：
        /// 1. 检查undoLogRecord是否为空，如果为空则记录警告并返回
        /// 2. 记录开始执行回滚的日志（包含表名和SQL类型）
        /// 3. 根据SqlType执行相应的回滚操作：
        ///    - INSERT：调用ExecuteDeleteForInsertAsync删除新增的数据
        ///    - UPDATE：调用ExecuteUpdateForUpdateAsync恢复到before image
        ///    - DELETE：调用ExecuteInsertForDeleteAsync重新插入删除的数据
        ///    - 其他：记录警告日志（不支持的SQL类型）
        /// 4. 记录回滚执行成功的日志
        /// 5. 如果发生异常，记录错误日志并重新抛出异常
        ///
        /// SQL类型与回滚操作映射：
        /// ```
        /// 原始SQL     | SQL类型       | 回滚操作                | 使用的Image
        /// ------------|--------------|------------------------|-------------
        /// INSERT      | SqlType.INSERT| DELETE (删除新增的数据)  | AfterImage
        /// UPDATE      | SqlType.UPDATE| UPDATE (恢复到修改前)    | BeforeImage
        /// DELETE      | SqlType.DELETE| INSERT (重新插入)       | BeforeImage
        /// ```
        ///
        /// 调用时机：
        /// - 第二阶段回滚：BranchRollback处理器调用
        /// - TC通知RM执行回滚时
        /// - 在数据库事务中执行（确保原子性）
        ///
        /// 注意事项：
        /// - 必须在数据库事务中执行，回滚失败会导致事务回滚
        /// - undoLogRecord为null时不抛出异常，只记录警告
        /// - 不支持的SQL类型不会执行回滚，只记录警告
        /// - 回滚失败会抛出异常，由调用方处理（通常是重试或人工介入）
        /// - 所有回滚操作都会记录详细日志，便于问题排查
        /// </summary>
        /// <param name="connection">数据库连接，必须已打开并在事务中</param>
        /// <param name="undoLogRecord">undo_log记录，包含SQL类型和before/after image</param>
        /// <exception cref="Exception">回滚执行失败时抛出</exception>
        public async Task ExecuteRollbackAsync(DbConnection connection, UndoLogRecord undoLogRecord)
        {
            if (undoLogRecord == null)
            {
                _logger.LogWarning("UndoLogRecord为空，无法执行回滚");
                return;
            }

            _logger.LogInformation($"开始执行回滚: 表={undoLogRecord.TableName}, SQL类型={undoLogRecord.SqlType}");

            try
            {
                switch (undoLogRecord.SqlType)
                {
                    case SqlType.INSERT:
                        await ExecuteDeleteForInsertAsync(connection, undoLogRecord);
                        break;

                    case SqlType.UPDATE:
                        await ExecuteUpdateForUpdateAsync(connection, undoLogRecord);
                        break;

                    case SqlType.DELETE:
                        await ExecuteInsertForDeleteAsync(connection, undoLogRecord);
                        break;

                    default:
                        _logger.LogWarning($"不支持的SQL类型: {undoLogRecord.SqlType}");
                        break;
                }

                _logger.LogInformation($"回滚执行成功: 表={undoLogRecord.TableName}, SQL类型={undoLogRecord.SqlType}");
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, $"回滚执行失败: 表={undoLogRecord.TableName}, SQL类型={undoLogRecord.SqlType}");
                throw;
            }
        }

        /// <summary>
        /// 对INSERT操作执行DELETE回滚
        ///
        /// 将INSERT语句插入的数据删除，恢复到INSERT执行前的状态。
        ///
        /// 执行流程：
        /// 1. 从undoLogRecord中获取AfterImage（INSERT后的数据镜像）
        /// 2. 检查AfterImage是否为空或没有数据行
        /// 3. 遍历AfterImage中的每一行数据：
        ///    - 根据主键构建WHERE条件（调用BuildWhereClauseByPrimaryKey）
        ///    - 生成DELETE SQL：DELETE FROM table WHERE pk=?
        ///    - 记录调试日志
        ///    - 创建DbCommand并设置SQL
        ///    - 添加参数（调用AddParametersFromRow，只添加主键参数）
        ///    - 执行DELETE操作
        ///
        /// 为什么使用AfterImage：
        /// - INSERT操作没有before image（插入前数据不存在）
        /// - AfterImage包含INSERT后的完整数据，包括主键值
        /// - 通过AfterImage的主键可以精确定位要删除的行
        ///
        /// SQL示例：
        /// ```sql
        /// -- 原始INSERT
        /// INSERT INTO user_table (id, name, balance) VALUES (1001, 'Alice', 1000);
        ///
        /// -- 回滚DELETE（根据AfterImage生成）
        /// DELETE FROM user_table WHERE id = 1001;
        /// ```
        ///
        /// 注意事项：
        /// - AfterImage为空或没有数据行时，记录警告并返回（不抛出异常）
        /// - 只使用主键列构建WHERE条件（确保精确删除）
        /// - 如果数据已被其他事务修改或删除，DELETE可能影响0行
        /// - 支持批量回滚（AfterImage可能包含多行数据）
        /// - 参数化查询防止SQL注入
        /// </summary>
        /// <param name="connection">数据库连接，必须已打开并在事务中</param>
        /// <param name="undoLogRecord">undo_log记录，包含AfterImage</param>
        private async Task ExecuteDeleteForInsertAsync(DbConnection connection, UndoLogRecord undoLogRecord)
        {
            var afterImage = undoLogRecord.AfterImage;
            if (afterImage == null || !afterImage.Rows.Any())
            {
                _logger.LogWarning("AfterImage为空，无法执行DELETE回滚");
                return;
            }

            foreach (var row in afterImage.Rows)
            {
                var whereClause = BuildWhereClauseByPrimaryKey(afterImage.TableMeta, row);
                var sql = $"DELETE FROM {afterImage.TableName} WHERE {whereClause}";

                _logger.LogDebug($"执行DELETE回滚SQL: {sql}");

                using var command = connection.CreateCommand();
                command.CommandText = sql;
                command.CommandType = CommandType.Text;

                AddParametersFromRow(command, row, afterImage.TableMeta.PrimaryKeyColumns);

                await command.ExecuteNonQueryAsync();
            }
        }

        /// <summary>
        /// 对UPDATE操作执行UPDATE回滚
        ///
        /// 将UPDATE语句修改的数据恢复到修改前的状态。
        ///
        /// 执行流程：
        /// 1. 从undoLogRecord中获取BeforeImage（UPDATE前的数据镜像）
        /// 2. 从undoLogRecord中获取AfterImage（UPDATE后的数据镜像）
        /// 3. 检查BeforeImage是否为空或没有数据行
        /// 4. 遍历BeforeImage中的每一行数据：
        ///    - 构建SET子句（调用BuildSetClause，只包含非主键列）
        ///    - 构建WHERE子句（调用BuildWhereClauseByPrimaryKey，只包含主键列）
        ///    - 生成UPDATE SQL：UPDATE table SET col1=?, col2=? WHERE pk=?
        ///    - 记录调试日志
        ///    - 创建DbCommand并设置SQL
        ///    - 添加参数（调用AddParametersFromRow，包含所有列）
        ///    - 执行UPDATE操作
        ///
        /// 为什么使用BeforeImage：
        /// - BeforeImage包含UPDATE前的数据状态
        /// - 回滚就是将数据恢复到BeforeImage的状态
        /// - BeforeImage包含所有列的值，包括主键和非主键列
        ///
        /// SQL示例：
        /// ```sql
        /// -- 原始UPDATE
        /// UPDATE user_table SET balance=800, name='Bob' WHERE id = 1001;
        ///
        /// -- BeforeImage: {id:1001, balance:1000, name:'Alice'}
        /// -- AfterImage:  {id:1001, balance:800, name:'Bob'}
        ///
        /// -- 回滚UPDATE（根据BeforeImage生成）
        /// UPDATE user_table SET balance=1000, name='Alice' WHERE id = 1001;
        /// ```
        ///
        /// 脏写检测：
        /// - 可选：在回滚前查询当前数据，与AfterImage对比
        /// - 如果当前数据与AfterImage不一致，说明数据被其他事务修改
        /// - 这种情况下需要人工介入（当前实现未包含脏写检测）
        ///
        /// 注意事项：
        /// - BeforeImage为空或没有数据行时，记录警告并返回（不抛出异常）
        /// - SET子句只包含非主键列（主键不能修改）
        /// - WHERE子句只包含主键列（确保精确更新）
        /// - 支持批量回滚（BeforeImage可能包含多行数据）
        /// - 参数化查询防止SQL注入
        /// - 如果数据已被其他事务删除，UPDATE可能影响0行
        /// </summary>
        /// <param name="connection">数据库连接，必须已打开并在事务中</param>
        /// <param name="undoLogRecord">undo_log记录，包含BeforeImage和AfterImage</param>
        private async Task ExecuteUpdateForUpdateAsync(DbConnection connection, UndoLogRecord undoLogRecord)
        {
            var beforeImage = undoLogRecord.BeforeImage;
            var afterImage = undoLogRecord.AfterImage;

            if (beforeImage == null || !beforeImage.Rows.Any())
            {
                _logger.LogWarning("BeforeImage为空，无法执行UPDATE回滚");
                return;
            }

            foreach (var beforeRow in beforeImage.Rows)
            {
                var setClause = BuildSetClause(beforeRow, beforeImage.TableMeta.PrimaryKeyColumns);
                var whereClause = BuildWhereClauseByPrimaryKey(beforeImage.TableMeta, beforeRow);
                var sql = $"UPDATE {beforeImage.TableName} SET {setClause} WHERE {whereClause}";

                _logger.LogDebug($"执行UPDATE回滚SQL: {sql}");

                using var command = connection.CreateCommand();
                command.CommandText = sql;
                command.CommandType = CommandType.Text;

                AddParametersFromRow(command, beforeRow);

                await command.ExecuteNonQueryAsync();
            }
        }

        /// <summary>
        /// 对DELETE操作执行INSERT回滚
        ///
        /// 将DELETE语句删除的数据重新插入，恢复到DELETE执行前的状态。
        ///
        /// 执行流程：
        /// 1. 从undoLogRecord中获取BeforeImage（DELETE前的数据镜像）
        /// 2. 检查BeforeImage是否为空或没有数据行
        /// 3. 遍历BeforeImage中的每一行数据：
        ///    - 提取所有字段名，构建列名列表：col1, col2, col3
        ///    - 提取所有字段名，构建参数列表：@col1, @col2, @col3
        ///    - 生成INSERT SQL：INSERT INTO table (col1, col2, col3) VALUES (@col1, @col2, @col3)
        ///    - 记录调试日志
        ///    - 创建DbCommand并设置SQL
        ///    - 添加参数（调用AddParametersFromRow，包含所有列）
        ///    - 执行INSERT操作
        ///
        /// 为什么使用BeforeImage：
        /// - DELETE操作没有after image（删除后数据不存在）
        /// - BeforeImage包含DELETE前的完整数据，包括所有列的值
        /// - 通过BeforeImage可以完整恢复被删除的数据
        ///
        /// SQL示例：
        /// ```sql
        /// -- 原始DELETE
        /// DELETE FROM user_table WHERE id = 1001;
        ///
        /// -- BeforeImage: {id:1001, balance:1000, name:'Alice'}
        ///
        /// -- 回滚INSERT（根据BeforeImage生成）
        /// INSERT INTO user_table (id, balance, name) VALUES (1001, 1000, 'Alice');
        /// ```
        ///
        /// 主键冲突处理：
        /// - 如果主键已存在（其他事务插入了相同主键的数据），INSERT会失败
        /// - 这种情况通常不应该发生（全局锁应该防止并发冲突）
        /// - 如果发生，需要人工介入处理
        ///
        /// 注意事项：
        /// - BeforeImage为空或没有数据行时，记录警告并返回（不抛出异常）
        /// - INSERT包含所有列（包括主键和非主键列）
        /// - 支持批量回滚（BeforeImage可能包含多行数据）
        /// - 参数化查询防止SQL注入
        /// - 如果主键已存在，INSERT会抛出主键冲突异常
        /// - 确保BeforeImage包含完整的列信息（NULL值也要包含）
        /// </summary>
        /// <param name="connection">数据库连接，必须已打开并在事务中</param>
        /// <param name="undoLogRecord">undo_log记录，包含BeforeImage</param>
        private async Task ExecuteInsertForDeleteAsync(DbConnection connection, UndoLogRecord undoLogRecord)
        {
            var beforeImage = undoLogRecord.BeforeImage;
            if (beforeImage == null || !beforeImage.Rows.Any())
            {
                _logger.LogWarning("BeforeImage为空，无法执行INSERT回滚");
                return;
            }

            foreach (var row in beforeImage.Rows)
            {
                var columns = string.Join(", ", row.Fields.Select(f => f.Name));
                var parameters = string.Join(", ", row.Fields.Select(f => $"@{f.Name}"));
                var sql = $"INSERT INTO {beforeImage.TableName} ({columns}) VALUES ({parameters})";

                _logger.LogDebug($"执行INSERT回滚SQL: {sql}");

                using var command = connection.CreateCommand();
                command.CommandText = sql;
                command.CommandType = CommandType.Text;

                AddParametersFromRow(command, row);

                await command.ExecuteNonQueryAsync();
            }
        }

        /// <summary>
        /// 构建WHERE子句（基于主键）
        ///
        /// 根据表的主键列和数据行，生成WHERE条件字符串。
        ///
        /// 执行流程：
        /// 1. 创建条件列表
        /// 2. 遍历tableMeta.PrimaryKeyColumns中的每个主键列名
        /// 3. 为每个主键列生成条件：pkColumn = @pkColumn
        /// 4. 将所有条件用" AND "连接
        /// 5. 返回完整的WHERE子句（不包含"WHERE"关键字）
        ///
        /// 生成示例：
        /// ```
        /// // 单主键表
        /// 输入：PrimaryKeyColumns = ["id"]
        /// 输出："id = @id"
        ///
        /// // 复合主键表
        /// 输入：PrimaryKeyColumns = ["user_id", "product_id"]
        /// 输出："user_id = @user_id AND product_id = @product_id"
        /// ```
        ///
        /// 使用场景：
        /// - ExecuteDeleteForInsertAsync：生成DELETE的WHERE条件
        /// - ExecuteUpdateForUpdateAsync：生成UPDATE的WHERE条件
        ///
        /// 注意事项：
        /// - 返回值不包含"WHERE"关键字，调用方需要自行添加
        /// - 使用参数化查询，参数名格式为@列名
        /// - 支持复合主键（多个主键列用AND连接）
        /// - 主键列表不能为空（表必须有主键）
        /// </summary>
        /// <param name="tableMeta">表元数据，包含主键列名列表</param>
        /// <param name="row">数据行，包含主键列的值（未直接使用，参数值在AddParametersFromRow中添加）</param>
        /// <returns>WHERE子句字符串，不包含"WHERE"关键字</returns>
        private string BuildWhereClauseByPrimaryKey(TableMeta tableMeta, Row row)
        {
            var conditions = new List<string>();

            foreach (var pkColumn in tableMeta.PrimaryKeyColumns)
            {
                conditions.Add($"{pkColumn} = @{pkColumn}");
            }

            return string.Join(" AND ", conditions);
        }

        /// <summary>
        /// 构建SET子句（用于UPDATE语句）
        ///
        /// 根据数据行和主键列列表，生成UPDATE语句的SET子句。
        ///
        /// 执行流程：
        /// 1. 创建SET键值对列表
        /// 2. 遍历row.Fields中的每个字段
        /// 3. 如果字段不是主键列（不在primaryKeyColumns中）：
        ///    - 生成SET键值对：fieldName = @fieldName
        ///    - 添加到列表中
        /// 4. 将所有键值对用", "连接
        /// 5. 返回完整的SET子句（不包含"SET"关键字）
        ///
        /// 为什么排除主键列：
        /// - UPDATE语句不能修改主键值（主键是唯一标识）
        /// - 主键用于WHERE条件，而不是SET子句
        /// - 只有非主键列的值需要恢复
        ///
        /// 生成示例：
        /// ```
        /// // 输入行数据：{id:1001, balance:1000, name:'Alice'}
        /// // 主键列：["id"]
        /// 输出："balance = @balance, name = @name"
        ///
        /// // 输入行数据：{user_id:1, product_id:2, quantity:10, price:100}
        /// // 主键列：["user_id", "product_id"]
        /// 输出："quantity = @quantity, price = @price"
        /// ```
        ///
        /// 使用场景：
        /// - ExecuteUpdateForUpdateAsync：生成UPDATE的SET子句
        ///
        /// 注意事项：
        /// - 返回值不包含"SET"关键字，调用方需要自行添加
        /// - 使用参数化查询，参数名格式为@列名
        /// - 排除主键列（主键不可修改）
        /// - 如果所有列都是主键列，返回空字符串（实际不应该发生）
        /// - NULL值也会包含在SET子句中（通过参数传递DBNull.Value）
        /// </summary>
        /// <param name="row">数据行，包含所有列的值</param>
        /// <param name="primaryKeyColumns">主键列名列表，用于排除主键列</param>
        /// <returns>SET子句字符串，不包含"SET"关键字</returns>
        private string BuildSetClause(Row row, List<string> primaryKeyColumns)
        {
            var setPairs = new List<string>();

            foreach (var field in row.Fields)
            {
                if (!primaryKeyColumns.Contains(field.Name))
                {
                    setPairs.Add($"{field.Name} = @{field.Name}");
                }
            }

            return string.Join(", ", setPairs);
        }

        /// <summary>
        /// 从行数据添加参数到命令
        ///
        /// 将数据行中的字段值作为参数添加到DbCommand中，支持可选的列过滤。
        ///
        /// 执行流程：
        /// 1. 遍历row.Fields中的每个字段
        /// 2. 对每个字段进行过滤检查：
        ///    - 如果columnFilter为null：添加所有字段
        ///    - 如果columnFilter不为null：只添加在columnFilter中的字段
        /// 3. 对于需要添加的字段：
        ///    - 创建DbParameter对象
        ///    - 设置参数名：@字段名
        ///    - 设置参数值：field.Value，如果为null则使用DBNull.Value
        ///    - 将参数添加到command.Parameters集合中
        ///
        /// 参数过滤用途：
        /// - 无过滤（columnFilter=null）：添加所有列的参数
        ///   - 用于：INSERT回滚、UPDATE回滚
        /// - 主键过滤（columnFilter=PrimaryKeyColumns）：只添加主键列的参数
        ///   - 用于：DELETE回滚（WHERE子句只需要主键）
        ///
        /// NULL值处理：
        /// - C#的null无法直接传递给数据库
        /// - 使用?? 运算符转换：field.Value ?? DBNull.Value
        /// - 确保数据库能正确识别NULL值
        ///
        /// 使用示例：
        /// ```csharp
        /// // 示例1：添加所有列的参数（用于INSERT和UPDATE）
        /// AddParametersFromRow(command, row);
        ///
        /// // 示例2：只添加主键列的参数（用于DELETE的WHERE条件）
        /// AddParametersFromRow(command, row, tableMeta.PrimaryKeyColumns);
        /// ```
        ///
        /// 参数命名规则：
        /// - 参数名格式：@字段名
        /// - 必须与SQL中的占位符一致
        /// - 如BuildWhereClauseByPrimaryKey生成"id = @id"，此方法添加名为@id的参数
        ///
        /// 使用场景：
        /// - ExecuteDeleteForInsertAsync：添加主键参数（DELETE WHERE）
        /// - ExecuteUpdateForUpdateAsync：添加所有列参数（UPDATE SET + WHERE）
        /// - ExecuteInsertForDeleteAsync：添加所有列参数（INSERT VALUES）
        ///
        /// 注意事项：
        /// - 参数名必须与SQL占位符一致（@字段名）
        /// - NULL值会自动转换为DBNull.Value
        /// - 参数化查询防止SQL注入
        /// - columnFilter为null时添加所有字段
        /// - columnFilter非null时只添加在列表中的字段
        /// - 字段值的类型必须与数据库列类型兼容
        /// </summary>
        /// <param name="command">数据库命令对象</param>
        /// <param name="row">数据行，包含所有字段的值</param>
        /// <param name="columnFilter">可选的列名过滤列表，为null时添加所有列，非null时只添加列表中的列</param>
        private void AddParametersFromRow(DbCommand command, Row row, List<string> columnFilter = null)
        {
            foreach (var field in row.Fields)
            {
                if (columnFilter == null || columnFilter.Contains(field.Name))
                {
                    var parameter = command.CreateParameter();
                    parameter.ParameterName = $"@{field.Name}";
                    parameter.Value = field.Value ?? DBNull.Value;
                    command.Parameters.Add(parameter);
                }
            }
        }
    }
}
